package handler

import (
	"fmt"
	"github.com/keybase/go-ps"
	"github.com/shirou/gopsutil/v3/net"
	"github.com/shirou/gopsutil/v3/process"
	"github.com/tietang/props/v3/kvs"
	"log"
	"strconv"
	"strings"
)

var KeysServiceId = []string{"serviceId", "serviceID", "service.id", "appId", "appID", "app.id"}

type KV struct {
	Key, Value string
}
type ServiceProcess struct {
	Pid            int
	PPid           int
	Port           int
	ServiceId      string
	Addr           net.Addr
	HealthCheckUrl string
	Labels         map[string]KV
	Process        *process.Process
	//
	ListenedAddrs []net.Addr
}

func (s *ServiceProcess) Key() string {
	if s.ServiceId != "" {
		return s.ServiceId
	} else {
		return strconv.Itoa(s.Pid) + "_" + strconv.Itoa(s.PPid)
	}
}

func (s *ServiceProcess) Clone() *ServiceProcess {
	sp := &ServiceProcess{
		Pid:            s.Pid,
		PPid:           s.PPid,
		Port:           s.Port,
		ServiceId:      s.ServiceId,
		Addr:           s.Addr,
		HealthCheckUrl: s.HealthCheckUrl,
		Labels:         s.Labels,
		Process:        s.Process,
	}
	return sp
}

func (jp *ServiceProcess) ParseServiceId() {
	for _, s := range KeysServiceId {
		if v, ok := jp.Labels[s]; ok {
			jp.ServiceId = v.Value
			if v.Value != "" {
				break
			}
		}
	}
}

func (jp *ServiceProcess) ParseHealthCheckUrl() {

	if v, ok := jp.Labels["health.url"]; ok {
		jp.HealthCheckUrl = v.Value
	}
}
func (jp *ServiceProcess) ParsePort() {
	if v, ok := jp.Labels["server.port"]; ok {
		jp.Port, _ = strconv.Atoi(v.Value)
	}
}

type ProcessHandler interface {
	GetProcesses() ([]*ServiceProcess, error)
}

var replacer *strings.Replacer
var conv *strings.Replacer

func init() {
	replacer = strings.NewReplacer("Application", "", "App", "", "Main", "", "Bootstrap", "")
	conv = strings.NewReplacer("_", "-", ".", "-", ":", "-", " ", "-")
}

type BaseProcessHandler struct {
}

func (j *BaseProcessHandler) FileNameToServiceId(name string) string {
	names := strings.Split(name, ".")
	id := names[len(names)-1]
	return conv.Replace(id)
}
func (j *BaseProcessHandler) Replace(name string) string {

	return replacer.Replace(name)
}
func (j *BaseProcessHandler) Convert(name string) string {
	return conv.Replace(name)
}

func (j *BaseProcessHandler) GetAddrs(p *process.Process) []net.Addr {

	conns, err := p.Connections()
	if err != nil {
		log.Println(err)
		return nil
	}
	listenedAddrs := make([]net.Addr, 0)
	for _, c := range conns {
		addr := c.Laddr
		if c.Status == "LISTEN" && addr.Port > 0 {
			listenedAddrs = append(listenedAddrs, addr)
		}
	}
	return listenedAddrs

}

const KEY_SERVICE_SPECIFIED_NAMES = "service.include.specified.name.segments"
const KEY_SERVICE_SPECIFIED_PATHS = "service.include.specified.path.segments"
const KEY_SERVICE_SPECIFIED_ARG_NAME = "service.include.specified.arg.name"

// 参数中包含serviceid
type DefaultProcessHandler struct {
	BaseProcessHandler
	conf kvs.ConfigSource
}

func (p *DefaultProcessHandler) GetProcesses() ([]*ServiceProcess, error) {
	processes := make([]*ServiceProcess, 0)
	names := p.conf.Strings(KEY_SERVICE_SPECIFIED_NAMES)
	paths := p.conf.Strings(KEY_SERVICE_SPECIFIED_PATHS)
	argNames := p.conf.Strings(KEY_SERVICE_SPECIFIED_ARG_NAME)

	sysPss, err := ps.Processes()
	if err != nil {
		return processes, err
	}
	for _, pss := range sysPss {
		path, err := pss.Path()
		name := pss.Executable()
		pid := pss.Pid()
		if filterName(names, paths, name, path) {
			//processes=append(processes,)
			continue

		}
		p, err := process.NewProcess(int32(pid))
		if err != nil {
			fmt.Println(err)
			continue
		}
		cmds, err := p.CmdlineSlice()
		if err != nil {
			fmt.Println(err)
			continue
		}
		if filterOrgs(argNames, cmds) {
			processes = append(processes)
			continue
		}

	}

	return processes, nil
}

func filterOrgs(argNames []string, cmdlineSlice []string) bool {
	for _, arg := range argNames {
		for _, cmd := range cmdlineSlice {
			if strings.Contains(cmd, arg) {
				return true
			}
		}
	}

	return false
}
func filterName(names, paths []string, name, path string) bool {
	for _, seg := range names {
		if strings.Contains(name, seg) {
			return true
		}
	}
	for _, seg := range paths {
		if strings.Contains(path, seg) {
			return true
		}
	}
	return false
}

//
//
//type ServiceProcess interface {
//    GetPid() int
//    GetPPid() int
//    GetPort() int
//    GetServiceId() string
//    GetAddr() net.Addr
//    GetHealthCheckUrl() string
//    GetLabels() map[string]KV
//    GetProcess() *process.Process
//
//    SetPid(pid int)
//    SetPPid(ppid int)
//    SetPort(port int)
//    SetServiceId(serviceid string)
//    SetAddr(addr int)
//    SetHealthCheckUrl(healthCheckUrl string)
//    SetLabels(labels map[string]KV)
//    SetProcess(process *process.Process)
//
//    AddLabels(k string, kv KV)
//    GetLabel(k string) (KV, bool)
//}
//
//type BaseServiceProcess struct {
//    Pid            int
//    PPid           int
//    Port           int
//    ServiceId      string
//    Addr           net.Addr
//    HealthCheckUrl string
//    Labels         map[string]KV
//    Process        *process.Process
//}
//
//func (s *BaseServiceProcess) GetPid() int                  { return s.Pid }
//func (s *BaseServiceProcess) GetPPid() int                 { return s.PPid }
//func (s *BaseServiceProcess) GetPort() int                 { return s.Port }
//func (s *BaseServiceProcess) GetServiceId() string         { return s.ServiceId }
//func (s *BaseServiceProcess) GetAddr() net.Addr            { return s.Addr }
//func (s *BaseServiceProcess) GetHealthCheckUrl() string    { return s.HealthCheckUrl }
//func (s *BaseServiceProcess) GetLabels() map[string]KV     { return s.Labels }
//func (s *BaseServiceProcess) GetProcess() *process.Process { return s.Process }
//
//func (s *BaseServiceProcess) SetPid(pid int)                          { s.Pid = pid }
//func (s *BaseServiceProcess) SetPPid(ppid int)                        { s.PPid = ppid }
//func (s *BaseServiceProcess) SetPort(port int)                        { s.Port = port }
//func (s *BaseServiceProcess) SetServiceId(serviceId string)           { s.ServiceId = serviceId }
//func (s *BaseServiceProcess) SetAddr(addr net.Addr)                   { s.Addr = addr }
//func (s *BaseServiceProcess) SetHealthCheckUrl(healthCheckUrl string) { s.HealthCheckUrl = healthCheckUrl }
//func (s *BaseServiceProcess) SetLabels(labels map[string]KV)          { s.Labels = labels }
//func (s *BaseServiceProcess) SetProcess(process *process.Process)     { s.Process = process }
//func (s *BaseServiceProcess) AddLabels(k string, kv KV)               { s.Labels[k] = kv }
//func (s *BaseServiceProcess) GetLabel(k string) (KV, bool)            { kv, ok := s.Labels[k]; return kv, ok }
